﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework.Graphics;
using Minestorm.Evolved.Assets;
using Microsoft.Xna.Framework;
using Minestorm.Evolved.Scenes;
using Minestorm.Evolved.IoC;

namespace Minestorm.Evolved.SceneObjects
{
    public class MineFieldMapObject : SceneObject
    {
        protected Vector3 Position { get; set; }
        protected Vector3 Rotation { get; set; }
        protected Vector3 RotationRate { get; set; }
        protected float Scale { get; set; }

        public Vector3 GlobalPosition { get; set; }
        public bool Selected { get; set; }

        public MineFieldMapObject(Vector3 position, float scale, Vector3 rotation, Vector3 rotationRate)
        {
            Position = position;
            Scale = scale;
            Rotation = rotation;
            RotationRate = rotationRate;
        }
    }

    public class Asteroid : MineFieldMapObject
    {
        private Model _model;
        private ModelEffect _effect;
        private SolidColorEffect _effectOutline;
        private Texture2D _texture;

        public Asteroid(Vector3 position, float scale, Vector3 rotation, Vector3 rotationRate)
            : base(position, scale, rotation, rotationRate)
        { }

        public override void LoadContent()
        {
            var content = ObjectFactory.InstanceOf<IAssetManager>();

            _model = content.Load(Models.Asteroid);
            _effect = content.Load(Effects.Model);
            _effectOutline = content.Load(Effects.Basic);
            _texture = content.Load(Textures.Asteroid);

            _effect.Projection = Scene.Camera.Projection;
            _effect.Texture = _texture;

            _effectOutline.Projection = Scene.Camera.Projection;
            _effectOutline.Color = Color.Red;

            foreach (var mesh in _model.Meshes)
                foreach (var part in mesh.MeshParts)
                    part.Effect = _effect;

            base.LoadContent();
        }

        public override void Update(GameTime gameTime)
        {
            var x = Rotation.X + (float)gameTime.ElapsedGameTime.TotalMilliseconds * RotationRate.X;
            var y = Rotation.Y + (float)gameTime.ElapsedGameTime.TotalMilliseconds * RotationRate.Y;
            var z = Rotation.Z + (float)gameTime.ElapsedGameTime.TotalMilliseconds * RotationRate.Z;

            Rotation = new Vector3(x, y, z);

            base.Update(gameTime);
        }

        public override void Draw(GameTime gameTime)
        {
            _effect.View = Scene.Camera.View;
            _effectOutline.View = Scene.Camera.View;
            _effectOutline.CameraPosition = Scene.Camera.Position;

            var worldOutline = Matrix.Identity * Matrix.CreateScale(Scale) * Matrix.CreateScale(1.1f) * Matrix.CreateFromYawPitchRoll(Rotation.X, Rotation.Y, Rotation.Z) * Matrix.CreateTranslation(Position + GlobalPosition);

            var boneTransforms = new Matrix[_model.Bones.Count];

            _model.CopyAbsoluteBoneTransformsTo(boneTransforms);

            SaveGraphicsState(GraphicsStates.DepthStencilState);

            foreach (var mesh in _model.Meshes)
            {
                if (Selected)
                {
                    _effectOutline.World = boneTransforms[mesh.ParentBone.Index] * worldOutline;

                    GraphicsDevice.DepthStencilState = new DepthStencilState()
                    {
                        DepthBufferWriteEnable = false
                    };

                    foreach (var part in mesh.MeshParts)
                        part.Effect = _effectOutline;

                    mesh.Draw();
                }

                var world = Matrix.Identity * Matrix.CreateScale(Scale) * Matrix.CreateFromYawPitchRoll(Rotation.X, Rotation.Y, Rotation.Z) * Matrix.CreateTranslation(Position + GlobalPosition);

                _effect.World = boneTransforms[mesh.ParentBone.Index] * world;

                GraphicsDevice.DepthStencilState = new DepthStencilState()
                {
                    DepthBufferWriteEnable = true
                };

                foreach (var part in mesh.MeshParts)
                    part.Effect = _effect;

                mesh.Draw();
            }

            RestoreGraphicsState();

            base.Draw(gameTime);
        }
    }

    public class MineField : SceneObject
    {
        private MineFieldMapObject[] _mapObjects;
        private Vector3 _position;
        private bool _selected;
        private int _index;

        public MineField(MineFieldMapObject[] mapObjects, Vector3 position, int index, bool selected)
        {
            _mapObjects = mapObjects;

            _index = index;
            Selected = selected;

            foreach (var mapObject in _mapObjects)
                mapObject.GlobalPosition = position;

            _position = position;
        }

        public Vector3 Position { get { return _position; } }

        public override void LoadContent()
        {
            foreach (var mapObject in _mapObjects)
                mapObject.LoadContent();

            base.LoadContent();
        }

        public override void Update(GameTime gameTime)
        {
            foreach (var mapObject in _mapObjects)
                mapObject.Update(gameTime);

            base.Update(gameTime);
        }

        public override void Draw(GameTime gameTime)
        {
            foreach (var mapObject in _mapObjects)
                mapObject.Draw(gameTime);

            base.Draw(gameTime);
        }

        public override Scene Scene
        {
            get { return base.Scene; }
            set
            {
                base.Scene = value;

                foreach (var mapObject in _mapObjects)
                    mapObject.Scene = value;
            }
        }

        public override GraphicsDevice GraphicsDevice
        {
            get { return base.GraphicsDevice; }
            set
            {
                base.GraphicsDevice = value;

                foreach (var mapObject in _mapObjects)
                    mapObject.GraphicsDevice = value;
            }
        }

        public override SpriteBatch SpriteBatch
        {
            get { return base.SpriteBatch; }
            set
            {
                base.SpriteBatch = value;

                foreach (var mapObject in _mapObjects)
                    mapObject.SpriteBatch = value;
            }
        }

        public override IObjectFactory ObjectFactory
        {
            get { return base.ObjectFactory; }
            set
            {
                base.ObjectFactory = value;

                foreach (var mapObject in _mapObjects)
                    mapObject.ObjectFactory = value;
            }
        }

        public bool Selected
        {
            get { return _selected; }
            set
            {
                _selected = value;

                foreach (var mapObject in _mapObjects)
                    mapObject.Selected = value;
            }
        }
    }
}
